#include "linux/mm.h"
#include "linux/arch.h"
#include "linux/fork.h"
#include "linux/kernel.h"
#include "linux/interrupt.h"
#include "linux/error.h"
#include "linux/inet.h"
#include "linux/config.h"
#include "linux/proc.h"
#include "linux/wait.h"
#include "linux/timer.h"
#include "linux/string.h"
#include "linux/printk.h"
#include "linux/vfs.h"
#include "linux/syscall.h"
#include "linux/completion.h"
#include "linux/unistd.h"
#include "linux/list.h"
#include "linux/preempt.h"
#include "linux/sched.h"
#include "linux/thread_info.h"

#include "asm/pgtable.h"
#include "uapi/asm-generic/mman-common.h"

extern struct task_struct * pcb_list_init(void);
extern struct task_struct *alloc_pcb(void);
extern void *alloc_stack(void);

struct task_struct *old_task = NULL;
static struct task_struct sleep_proc_list_head = 
{
    .pid = -1,
    .next_sleep_proc = &sleep_proc_list_head,
    .prev_sleep_proc = &sleep_proc_list_head,
};
static unsigned int OS_TICKS = 0;
static unsigned int pid = 1;

#define DO_INIT_SP(sp,fn,args,lr,cpsr,pt_base)									\
		do{																		\
				(sp)=(sp)-4;/*r15*/												\
				*(volatile unsigned int *)(sp)=(unsigned int)(fn);/*r15*/		\
				(sp)=(sp)-4;/*cpsr*/											\
				*(volatile unsigned int *)(sp)=(unsigned int)(cpsr);/*r14*/		\
				(sp)=(sp)-4;/*lr*/												\
				*(volatile unsigned int *)(sp)=(unsigned int)(lr);				\
				(sp)=(sp)-4*13;/*r12,r11,r10,r9,r8,r7,r6,r5,r4,r3,r2,r1,r0*/	\
				*(volatile unsigned int *)(sp)=(unsigned int)(args);			\
		}while(0)

void DO_INIT_CONTEXT(struct pt_regs *regs, int (*fn)(void *), void *args, void (*lr)(void), unsigned long cpsr, unsigned long sp, unsigned long pt_base)
{
    regs->ARM_pc = (unsigned long)fn;
    regs->ARM_cpsr = cpsr;
    regs->ARM_lr = (unsigned long)lr;
    regs->ARM_sp = sp;
    regs->ARM_r0 = 0xa5;
    regs->ARM_r1 = 1;
    regs->ARM_r2 = 2;
    regs->ARM_r3 = 3;
    regs->ARM_r4 = 4;
    regs->ARM_r5 = 5;
    regs->ARM_r6 = 6;
    regs->ARM_r7 = 7;
    regs->ARM_r8 = 8;
    regs->ARM_r9 = 9;
    regs->ARM_r10 = 10;
    regs->ARM_fp = 11;
    regs->ARM_ip = 12;
}

void *get_process_sp()
{
	void *tmp;
    
    tmp = (void *)alloc_stack();
    printk("get_sp_addr: %x\r\n", (unsigned int)tmp);
	return tmp;
}

unsigned int get_cpsr(void)
{
		unsigned long p;
	asm volatile
    (
		"mrs %0,cpsr\n"
		:"=r"(p)
		:
	);
	return p;
}

void thread_exit(void)
{
    while (1)
    {
        //sleep(1);
    }
}

unsigned int get_new_pid(void)
{
    return pid++;
}

struct mm_struct *create_task_alloc_mm(void)
{
   struct mm_struct* mm = mm_alloc();
   mm->mmlist.next = &mm->mmlist;
   mm->mmlist.prev = &mm->mmlist;
    
   memcpy((void *)mm->pgd, (void *)swapper_pg_dir, 4096 * 4);

   return mm;
}

extern struct inode *root_inode;
int kernel_thread(int (*f)(void *), void *args)
{ 
    return do_fork(0, (unsigned long)f, NULL, (unsigned long)args);
}

void dump_all_task(void)
{

}

void __wake_up(wait_queue_t *wq)
{

}

void prepare_to_wait(wait_queue_t *wq, unsigned int state)
{
    wq->priv = current;
    current->state |= state;
}

void finish_wait(wait_queue_t *wq)
{
    struct task_struct *pcb;

    if (!wq || !wq->priv)
        return;

    pcb = wq->priv;
    pcb->state = PROCESS_READY;
}

void __wake_up_interruptible(wait_queue_t *wq)
{

    struct task_struct *pcb;

    if (!wq || !wq->priv)
        return;

    pcb = wq->priv;
    pcb->state = PROCESS_READY;
}

void __init_waitqueue_head(wait_queue_t *wq, char *name)
{
    spin_lock_init(&wq->lock);
    INIT_LIST_HEAD(&wq->task_list);
}


void process_timeout(void *data)
{
    set_task_state(data, PROCESS_READY);
}

/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 *定时器在栈上,如果进程异常退出没有清掉定时器就会引发异常
 *
 * 只验证了 MAX_SCHEDULE_TIMEOUT 分支,未验证超时分支
 */
long schedule_timeout(long timeout, unsigned int state)
{
    unsigned long expire;
    struct timer_list timer;

    switch (timeout)
    {
    case MAX_SCHEDULE_TIMEOUT:
        schedule();
        return timeout < 0 ? 0 : timeout;
    default:
        break;
    }
    
    expire = OS_TICKS + timeout;

    timer.function = process_timeout;
    timer.data = current;
    timer.expires = timeout;

    add_timer(&timer);
    schedule();
    del_timer(&timer);

    timeout = expire - OS_TICKS;
    
    return timeout < 0 ? 0 : timeout;
}

long do_wait_for_common(struct completion *x, long timeout, unsigned int state)
{
    wait_queue_t wq;

    if (!x->done)
    {
        init_waitqueue_head(&wq);
        wq.priv = current;
        list_add_tail(&wq.task_list, &x->wq.task_list);
        
        do
        {
            set_current_state(state);
            spin_unlock_irq(&x->wq.lock);
            timeout = schedule_timeout(timeout, state);
            spin_lock_irq(&x->wq.lock);
        } while (!x->done && timeout);

        if (!x->done)
            return timeout;
    }

    x->done--;
    return timeout ? 1 : 0;
}

long wait_for_common(struct completion *x, long timeout, int state)
{
    spin_lock_irq(&x->wq.lock);
    
    timeout = do_wait_for_common(x, timeout, state);
    
    spin_unlock_irq(&x->wq.lock);

    return timeout; 
}

void wait_for_completion(struct completion *x)
{
    wait_for_common(x, MAX_SCHEDULE_TIMEOUT, PROCESS_WAIT);
}

void complete(struct completion *x)
{
	unsigned long flags;
    wait_queue_t *wq;
    struct list_head *list;

    spin_lock_irqsave(&x->wq.lock, flags);  
   
    x->done++;
   
    if (!list_empty(&x->wq.task_list))
    {
        list = x->wq.task_list.next;
        wq = list_entry(list, wait_queue_t, task_list);
        list_del(list);

        set_task_state(wq->priv, PROCESS_READY);
    }
    spin_unlock_irqrestore(&x->wq.lock, flags);
}

void init_completion(struct completion *x)
{
    x->done = 0;
    init_waitqueue_head(&x->wq);
}


void print_sleep_list(void)
{
    struct task_struct *tmp;

    tmp = sleep_proc_list_head.next_sleep_proc;

    while (tmp != &sleep_proc_list_head)
    {
        printk("pid: %d sleep_time: %d\n", tmp->pid, tmp->sleep_time);
        tmp = tmp->next_sleep_proc;
    }
}

void add_current_to_sleep_list(void)
{
   sleep_proc_list_head.next_sleep_proc->prev_sleep_proc = current;
   
   current->next_sleep_proc = sleep_proc_list_head.next_sleep_proc;
   current->prev_sleep_proc = &sleep_proc_list_head;
  
   sleep_proc_list_head.next_sleep_proc = current;
}

void process_sleep(unsigned int sec)
{
	enter_critical();

	current->state |= PROCESS_SLEEP;
	current->sleep_time = sec * HZ;

    add_current_to_sleep_list();

	exit_critical();
	schedule();
}

void process_msleep(unsigned int m)
{
	enter_critical();

	current->state |= PROCESS_SLEEP;
	current->sleep_time = m * HZ / 1000;
	if (!current->sleep_time)
		current->sleep_time = 1;
    
    add_current_to_sleep_list();

	exit_critical();
	schedule();
}

void set_task_state(void *task, unsigned int state)
{
    struct task_struct *tsk = task;

	enter_critical();

	tsk->state = state;

	exit_critical();
}

void set_current_state(unsigned int state)
{
    set_task_state(current, state);
}

void clr_task_status(unsigned int status)
{
	enter_critical();

	current->state &= ~(status);

	exit_critical();
}

void update_sleeping_proc(void)
{
    struct task_struct *tmp;
	
    tmp = sleep_proc_list_head.next_sleep_proc;
	while (tmp != &sleep_proc_list_head)
	{
        if (tmp->sleep_time > 0)
            tmp->sleep_time--;
        if (tmp->sleep_time == 0)
        {
            tmp->state &= ~(PROCESS_SLEEP);
            tmp->next_sleep_proc->prev_sleep_proc = tmp->prev_sleep_proc; 
            tmp->prev_sleep_proc->next_sleep_proc = tmp->next_sleep_proc;
        }
        tmp = tmp->next_sleep_proc;
	}

}

void OS_Clock_Tick(void *arg)
{
    //
    OS_TICKS++;
    //

    timer_list_process();
    update_sleeping_proc();

    if (current->ticks > 0)	
        current->ticks--;
   
    if (current->ticks > 0)
        return;

    current->ticks = current->time_slice;
    

    current->flags |= _TIF_NEED_RESCHED;

}

unsigned int OS_Get_Kernel_Ticks(void)
{
    return OS_TICKS;
}
		
void panic(void)
{
	while (1)
		;
}

extern int socket_init(void);
extern int create_stdin_stdout_stderr_device(void);
extern int platform_bus_init(void);
extern int test_completion(void *arg);
extern void do_basic_setup(void);



